#ifndef ENGINE_H_
#define ENGINE_H_

#include <LuaPlus.h>
#include <vector>
#include <Windows.h>
#include <d3dx9.h>
#include "DirectXMesh.h"

class GameState;
class LineDrawer;
class btClock;
class ResourceManager;

class Engine
{
#pragma region Singleton Implementation

private:
    Engine();
    Engine (const Engine& other){}
    Engine& operator=(const Engine& other){}
    ~Engine(){}

public:
    /* The main singleton Get function */
    static Engine* Instance()
    {
        static Engine* g = NULL;
        if( g == NULL)
        {
            g = new Engine;
        }
        return g;
    }
    /* Tidying up memory */
    static void Kill(){delete Instance();} 

#pragma endregion

#pragma region Lua Implementation
private:
    static LuaPlus::LuaObject* StaticInitializeLuaMetaTable();
    static LuaPlus::LuaObject* Static_MetaTable;
    static LuaPlus::LuaObject* GetMetaTable(){return Static_MetaTable;}
    std::vector<LuaPlus::LuaObject*> m_staticLuaObjects;
#pragma endregion

#pragma  region Error Implementation

public:
    void Error(const std::string& line, const std::string& file,std::string* extras = 0);
    LPCTSTR StringToString(const char *p);
    void StringToString(short* buffer, const char *p );
    void StringToString(wchar_t* buf, const char* src);
private:

#pragma endregion

#pragma region D3D Implementation   

public:
    /* Get the D3D device */
    LPDIRECT3DDEVICE9& GetDevice(){return m_mainD3DDevice;}
private:
    /* Variables involved in the creation of the d3d rendering context */
    LPDIRECT3D9 m_tempD3DDevice;
    LPDIRECT3DDEVICE9 m_mainD3DDevice;

#pragma endregion

#pragma region Windows Implementation

public:
    /* Get the hardware window */
    HWND& GetWindow(){return m_hWindow;}
    /* Get the hardware instance */
    HINSTANCE& GetInstance(){return m_hInstance;}

private:
    /* The hardware instance */
    HINSTANCE  m_hInstance;
    /* The hardware window */
    HWND m_hWindow;
    WNDCLASSEX m_wind;   
    wchar_t* m_windowNameWide;
    char* m_windowName;

#pragma endregion
public:
    /* The return function for int main()*/
	int				Execute();
    /* Sets up the screen, first gamestate and SDL/OpenGl related specifics */
	bool			Initialise();
    /* Main Update function */
	void			Update();
    /* The main rendering function */
	void			Draw();
    /* Tidys up memory and singletons */
	void			CleanUp();
    /* Quits the current game */
    void            Quit();
    /* State Management Functions */
	void			ChangeState(GameState*);
	void			RequestChange(GameState*);
	void			ManageStates();
    GameState*      GetCurrentState() {return m_currentState;}
    /* Called from lua VM */
    void CreateWindowClass(const char* name);
    void CreateWindowObj(const char* label, bool FS, int x, int y);
    void CreateDevice(bool vSync);
    void InitializeDepthSurface(int maxX, int maxY);
    void InitializeInput();
    void PrintLuaMessage(const char* msg);
    void LoadAndSetDefaultTexture(const char* path, const char* name);
    void ShutDownInput();
    void LoseDevice();
    bool LOpenLuaFile(const char* path, const char* fileName);
    void ReleaseLuaTables();
    void ReleaseResources();
    void EndCurrentState();
    void Unregister();

    /* Utility functions */
    void SetRenderTarget(IDirect3DTexture9* target, bool clear);
    void ResetRenderTarget();

    /* Creates the vertex data for a unit cube */
    LPDIRECT3DVERTEXBUFFER9 CreateUnitCube(int* totalVertsMade);
    LPDIRECT3DVERTEXBUFFER9 CreateTexturedQuad(int* totalVertsMade);

    LineDrawer* GetLineDrawer(){return m_lineDrawer;}
    ResourceManager* GetResource() {return m_resourceManager;}
    bool LLoadSharedMesh(const char* path, const char* fileName);
    bool LLoadTexture(const char* path, const char* fileName);
    void GetCurrentMousePosition(int* arr)
    {
        arr[0] = m_mousePosition[0];
        arr[1] = m_mousePosition[1];
    }
    void SetMousePosition( unsigned short param1, unsigned short param2 )
    {
        m_mousePosition[0] = param1;
        m_mousePosition[1] = param2;
    }
    int GetScreenRes(int index){return m_screenSize[index];}

private:
    /* The boolean used to keep game running */
	bool			m_running;

    /* Gamestate pointers */
	GameState*		m_requestState;
	GameState*		m_currentState;

    /* Utility information */
    LineDrawer*     m_lineDrawer;
    ResourceManager*    m_resourceManager;
    int             m_mousePosition[2];
    int             m_screenSize[2];

    //This should be a class !
    btClock* m_clock;
    float m_time;
    int m_frameCounter;
public: /* So anyone can access the framerate */
    int m_frameDisplay;    
    float m_lastFrameDt;

private:
    /* For rendering to an offscreen buffer  */
    LPDIRECT3DSURFACE9 m_depthStencilSurface;
    LPDIRECT3DSURFACE9 m_oldDepthStencilSurface;
    IDirect3DSurface9* m_oldRenderTarget;
};

//Usefullness in MACRO form baby.

/*
    Do these generate functions calls? Or are theyn direct in-code replacements?
*/
namespace ENGINE
{
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define GET_TEXTURE(x) Engine::Instance()->GetResource()->GetTexture(x)
#define GET_MESH(x) Engine::Instance()->GetResource()->GetMesh(x)

#ifdef _DEBUG
static long hresult = 0;
static int iresult = 0;
#define TEST(x){ ENGINE::hresult = (x); if( ENGINE::hresult != S_OK) { Engine::Instance()->Error(TOSTRING(__LINE__), TOSTRING(__FILE__)); assert(0);}}
#define TEST_IF(x){ ENGINE::iresult = (x); if( ENGINE::iresult == 0) { Engine::Instance()->Error(TOSTRING(__LINE__), TOSTRING(__FILE__)); assert(0);}}
#endif

#ifndef _DEBUG
#define TEST(x)           { (x);}
#define TEST_IF(x)           { (x);}
#endif
}; //ENGINE

#endif //ENGINE_H_